//! trace log 记录宏实现

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{
    parse::{self},
    parse_macro_input,
    punctuated::Punctuated,
    token::Comma,
    Fields, Ident, ItemStruct, LitInt, LitStr, Token, Type,
};

#[derive(Debug)]
struct TraceArgs {
    // 事件类
    class: Ident,
    // 事件
    event: Ident,
    // 输出格式化字符
    fmt: LitStr,
    // 参数个数
    count_num: u16,
    // 参数
    args: Option<Vec<Ident>>,
}

impl parse::Parse for TraceArgs {
    fn parse(input: parse::ParseStream) -> syn::Result<Self> {
        let class = input.parse()?;
        input.parse::<Comma>()?;
        let event = input.parse()?;
        input.parse::<Comma>()?;
        let fmt: LitStr = input.parse()?;
        input.parse::<Comma>()?;
        let count: LitInt = input.parse()?;
        let count_num = count.base10_parse::<u16>()?;
        let args = if count_num == 0 {
            None
        } else {
            input.parse::<Comma>()?;
            Some(Punctuated::<Ident, Token![,]>::parse_terminated(input)?.into_iter().collect())
        };
        Ok(TraceArgs { class, event, fmt, count_num, args })
    }
}

/// 定义一个 `tracelog` 元数据
///
/// 一般格式为 `class name`, `event name`, `format string`, `args count`, `arg0,` `arg1`...
///
/// 其中`argx`需要为结构体中存在的成员, 并且是一般成员, 不存在泛型实现
///
/// 必须明确指定 args count 参数个数
///
/// # Panics
/// 不符合定义格式将会触发错误
#[proc_macro_attribute]
pub fn tracelog(attr: TokenStream, item: TokenStream) -> TokenStream {
    // trace 属性参数
    let trace_args = parse_macro_input!(attr as TraceArgs);
    // 结构体定义
    let struct_f = parse_macro_input!(item as ItemStruct);
    // 格式化参数对应 type
    let mut trace_args_type = Vec::new();

    if trace_args.count_num != 0 {
        if let Fields::Named(n) = struct_f.fields.clone() {
            for i in trace_args.args.as_ref().unwrap() {
                for arg in n.named.iter() {
                    let name = arg.ident.as_ref().unwrap();
                    if let Type::Path(x) = &arg.ty {
                        let ty = x.path.segments.first().unwrap().ident.clone();
                        if i == name {
                            trace_args_type.push(ty);
                            break;
                        }
                    }
                }
            }
        } else {
            return parse::Error::new(Span::call_site(), "No support").to_compile_error().into();
        }
    }

    // 格式化输出函数
    let ident_fmt = Ident::new(&format!("trace_{}_fmt", trace_args.event), Span::call_site());

    // 记录 trace 函数
    let ident_record_raw = Ident::new(&format!("{}_raw", trace_args.event), Span::call_site());
    // 构造并调用 trace
    let ident_record = Ident::new(&format!("trace_{}", trace_args.event), Span::call_site());

    // 结构体 ident
    let struct_ident = struct_f.ident.clone();

    let fmt_raw = trace_args.fmt.value();
    // 格式化字符串
    let fmt = fmt_raw.as_str();

    let args_ident_some = trace_args.args;
    // 格式化参数 ident
    let args_ident = if let Some(arg) = args_ident_some { arg } else { Vec::new() };

    let class = trace_args.class.to_string();
    let event = trace_args.event.to_string();
    let trace_pointer =
        Ident::new(&format!("{}_{}", trace_args.class, trace_args.event), Span::call_site());
    let trace_pointer_register =
        Ident::new(&format!("{}_register", trace_pointer), Span::call_site());

    quote!(
        static #trace_pointer: std::sync::LazyLock<std::sync::Arc<std::sync::atomic::AtomicBool>> = std::sync::LazyLock::new(|| {
           std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false))
        });

        #[tracelog::ctor]
        fn #trace_pointer_register() {
            if tracelog::tracelog_enable() {
                tracelog::trace_point_push(#class.to_string(), #event.to_string(), #trace_pointer.clone());
            }
        }

        #[inline(always)]
        fn #ident_fmt(meta: &tracelog::TraceMetaData) -> String {
            unsafe {
                let mut new = meta.data;
                format!(#fmt,
                    #(
                        {
                            let this = new as *mut #trace_args_type;
                            new = new.add(std::mem::size_of::<#trace_args_type>());
                            this.read()
                        },
                    )*
                )
            }
        }

        impl #struct_ident {
            #[inline(always)]
            fn #ident_record_raw(&self) {
                if tracelog::tracelog_enable() {
                    if #trace_pointer.load(std::sync::atomic::Ordering::Relaxed) {
                        let size = 0 #(+ std::mem::size_of_val(&self.#args_ident))*;
                        let meta = tracelog::TraceMetaData::create(size, #ident_fmt);
                        unsafe {
                            let mut new = meta.data;
                            #(
                                let this = new as *mut #trace_args_type;
                                this.write(self.#args_ident.clone());
                                new = new.add(std::mem::size_of::<#trace_args_type>());
                            )*
                        }
                        tracelog::trace_metadata_push(meta);
                    }
                }
            }

            #[allow(clippy::ptr_arg)]
            #[inline(always)]
            pub(crate) fn #ident_record(#(#args_ident: &#trace_args_type,)*) {
                if tracelog::tracelog_enable() {
                    let mut trace = Self::default();
                    #(
                        trace.#args_ident = #args_ident.clone();
                    )*
                    trace.#ident_record_raw();
                }
            }
        }

        #struct_f
    )
    .into()
}
